package yiyun

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/goharbor/harbor/src/common/dao"
	"github.com/goharbor/harbor/src/common/models"
	"github.com/goharbor/harbor/src/core/api"
	"golang.org/x/net/context/ctxhttp"
	"golang.org/x/oauth2"
	"io"
	"io/ioutil"
	"mime"
	"net/http"
	"net/url"
	"os"
	"strconv"
	"strings"
	"time"
)

var (
	//userInfoURL = "http://172.16.160.70:29088/auth/profile"
	//authURL     = "http://172.16.160.70:29088/auth/authorize"
	//tokenURL    = "http://172.16.160.70:29088/auth/accessToken"
	//YIYUN_USER_INFO_URL=http://172.16.160.70:29088/auth/profile
	//YIYUN_AUTH_URL=http://172.16.160.70:29088/auth/authorize
	//YIYUN_TOKEN_URL=http://172.16.160.70:29088/auth/accessToken
	//YIYUN_CLIENT_ID=yoyo-harbor-41
	//YIYUN_CLIENT_SECRET=oheeyA2uyFJlHwyPCT
	userInfoURL = ""
	authURL     = ""
	tokenURL    = ""
	clientId	= ""
	clientSecret= ""
	appUrl		= ""
)

type Yiyun struct {
	api.BaseController
}

func init() {
	UserInfoUrl := os.Getenv("YIYUN_USER_INFO_URL")
	if len(UserInfoUrl) != 0 {
		userInfoURL = UserInfoUrl
	}
	fmt.Println("UserInfoUrl "+UserInfoUrl)
	AuthUrl := os.Getenv("YIYUN_AUTH_URL")
	if len(AuthUrl) != 0 {
		authURL = AuthUrl
	}
	fmt.Println("AuthUrl "+AuthUrl)
	TokenUrl := os.Getenv("YIYUN_TOKEN_URL")
	if len(TokenUrl) != 0 {
		tokenURL = TokenUrl
	}
	fmt.Println("TokenUrl "+TokenUrl)
	ClientId := os.Getenv("YIYUN_CLIENT_ID")
	if len(ClientId) != 0 {
		clientId = ClientId
	}
	fmt.Println("ClientId "+ClientId)
	ClientSecret := os.Getenv("YIYUN_CLIENT_SECRET")
	if len(ClientSecret) != 0 {
		clientSecret = ClientSecret
	}
	fmt.Println("ClientSecret "+ClientSecret)
	AppUrl := os.Getenv("YIYUN_APP_URL")
	if len(AppUrl) !=0 {
		appUrl = AppUrl
	}
	fmt.Println("AppUrl "+AppUrl)
	fmt.Println("yiyun sso init done")

}

func (g *Yiyun) GetAccessToken(ctx context.Context,code string) (*oauth2.Token, error){
	if clientId == ""{
		return nil,errors.New("yiyun client_id is empty")
	}
	if clientSecret == ""{
		return nil,errors.New("yiyun client_secret is empty")
	}
	if tokenURL == ""{
		return nil,errors.New("yiyun token url is empty")
	}
	v := url.Values{
		"grant_type": {"authorization_code"},
		"code":       {code},
	}

	//v.Set("client_id", "yoyo-harbor-41")
	//v.Set("client_secret", "oheeyA2uyFJlHwyPCT")

	v.Set("client_id", clientId)
	v.Set("client_secret", clientSecret)

	req, err := http.NewRequest("POST", tokenURL, strings.NewReader(v.Encode()))
	if err != nil {
		return nil, err
	}
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")

	if err != nil {
		return nil, err
	}
	r, err := ctxhttp.Do(ctx, http.DefaultClient, req)
	if err != nil {
		return nil, err
	}
	body, err := ioutil.ReadAll(io.LimitReader(r.Body, 1<<20))
	r.Body.Close()
	if err != nil {
		return nil, fmt.Errorf("oauth2: cannot fetch token: %v", err)
	}
	if code := r.StatusCode; code < 200 || code > 299 {
		return nil, fmt.Errorf("oauth2: cannot fetch token: %v", code)
	}

	var token *oauth2.Token
	content, _, _ := mime.ParseMediaType(r.Header.Get("Content-Type"))
	switch content {
	case "application/x-www-form-urlencoded", "text/plain":
		vals, err := url.ParseQuery(string(body))
		if err != nil {
			return nil, err
		}
		token = &oauth2.Token{
			AccessToken:  vals.Get("access_token"),
			TokenType:    vals.Get("token_type"),
			RefreshToken: vals.Get("refresh_token"),
		}
		e := vals.Get("expires_in")
		expires, _ := strconv.Atoi(e)
		if expires != 0 {
			token.Expiry = time.Now().Add(time.Duration(expires) * time.Second)
		}
	default:

		var yy YiyunTokenResult
		err:=json.Unmarshal(body,&yy)
		if err != nil {
			return nil,err
		}

		token = &oauth2.Token{
			AccessToken:  yy.Data.AccessToken,
			TokenType:    "",
			RefreshToken: yy.Data.RefreshToken,
		}
		if yy.Data.RefreshExpiresIn != 0 {
			token.Expiry = time.Now().Add(time.Duration(yy.Data.RefreshExpiresIn) * time.Second)
		}
	}
	if token.AccessToken == "" {
		return nil, errors.New("oauth2: server response missing access_token")
	}
	return token, nil
}



func (g *Yiyun) getUserProfile(ctx context.Context,accessToken string) (*YiyunUserProfileResult, error){

	if userInfoURL == ""{
		return nil,errors.New("yiyun userInfo url is empty")
	}
	v := url.Values{
		"access_token": {accessToken},
	}

	req, err := http.NewRequest("GET", userInfoURL+"?access_token="+accessToken, strings.NewReader(v.Encode()))
	if err != nil {
		return nil, err
	}
	//req.Header.Set("Content-Type", "application/x-www-form-urlencoded")

	if err != nil {
		return nil, err
	}

	r, err := ctxhttp.Do(ctx, http.DefaultClient, req)
	if err != nil {
		return nil, err
	}
	body, err := ioutil.ReadAll(io.LimitReader(r.Body, 1<<20))
	r.Body.Close()
	if err != nil {
		return nil, err
	}
	if code := r.StatusCode; code < 200 || code > 299 {
		return nil, fmt.Errorf("oauth2: cannot fetch token: %v", code)
	}
	var userprofile YiyunUserProfileResult
	err = json.Unmarshal(body,&userprofile)
	if err != nil {
		return nil,err
	}

	return &userprofile,nil
}


func (g *Yiyun) Callback(){
	fmt.Println("debug info================================")
	fmt.Println(g.Ctx.Request.URL.RawPath)
	fmt.Println(g.Ctx.Request.URL.Query().Get("code"))
	code := g.Ctx.Request.URL.Query().Get("code")
	//code := g.GetString("code")
	ctx := context.TODO()
	token, err := g.GetAccessToken(ctx, code)
	if err != nil {
		g.CustomAbort(http.StatusInternalServerError, err.Error())
	}

	yiyunUserProfileResult, err := g.getUserProfile(ctx, token.AccessToken)
	if err != nil {
		g.CustomAbort(http.StatusInternalServerError, err.Error())
	}
	fmt.Printf("yiyunUserProfileResult  =====%+v\n",yiyunUserProfileResult)
	user,err := dao.GetUser(*&models.User{
		Username: yiyunUserProfileResult.Data.Account,
	})
	fmt.Printf("user  =====%+v\n",user)
	if user == nil {

		if len(yiyunUserProfileResult.Data.Email)==0 {
			g.CustomAbort(http.StatusInternalServerError, "email is empty,please contact yiyun administrator")
		}
		_,err := dao.Register(*&models.User{
			Username: yiyunUserProfileResult.Data.Account,
			Password: "Harbor12345",
			Email: yiyunUserProfileResult.Data.Email,
		})
		if err != nil {
			g.CustomAbort(http.StatusInternalServerError, err.Error())
		}
	}
	user,err = dao.GetUser(*&models.User{
		Username: yiyunUserProfileResult.Data.Account,
	})
	if err != nil {
		g.CustomAbort(http.StatusInternalServerError, err.Error())
	}
	fmt.Printf("user  =====%+v\n",user)
	g.PopulateUserSession(*user)

	g.Ctx.ResponseWriter.Header().Add("Content-Type", "text/html; charset=utf-8")
	g.Ctx.Redirect(302,"/")
}

type SsoS struct {
	ssourl string
}
func (g *Yiyun) SsoUrl() {
	fmt.Println("sso url ()" )
	sso :=authURL +
		"?state=yiyun&client_id=" +
		clientId +
		"&response_type=code&redirect_uri=" +
		appUrl +
		"/api/oauth/redirect/yiyun"
	fmt.Println("sso url " + sso)

	g.Data["json"]=sso
	g.ServeJSON()

}
